#include "viml/builtin_string.h"
#include "viml/value.h"
#include <gtest/gtest.h>
#include <vector>

namespace viml {

// Test for len function with string
TEST(BuiltinStringTest, LenString) {
    // Test with normal string
    std::vector<Value> args = {Value::string("hello")};
    Value result = len_string_builtin(args);
    EXPECT_TRUE(result.is_number());
    EXPECT_EQ(result.as_number(), 5);
    
    // Test with empty string
    args[0] = Value::string("");
    result = len_string_builtin(args);
    EXPECT_TRUE(result.is_number());
    EXPECT_EQ(result.as_number(), 0);
    
    // Test with unicode string (note: this counts bytes, not characters)
    args[0] = Value::string("你好世界");
    result = len_string_builtin(args);
    EXPECT_TRUE(result.is_number());
    // This will count bytes, not characters, which is 12 for 4 Chinese characters in UTF-8
    EXPECT_EQ(result.as_number(), 12);
    
    // Test with wrong number of arguments
    std::vector<Value> empty_args;
    EXPECT_THROW(len_string_builtin(empty_args), std::runtime_error);
    
    std::vector<Value> too_many_args = {Value::string("test"), Value::string("test2")};
    EXPECT_THROW(len_string_builtin(too_many_args), std::runtime_error);
    
    // Test with non-string argument (but valid for len - should work with list)
    std::vector<Value> list_args = {Value::list()};
    result = len_string_builtin(list_args);
    EXPECT_TRUE(result.is_number());
    EXPECT_EQ(result.as_number(), 0);
    
    // Test with non-string argument that's invalid for len
    std::vector<Value> non_string_args = {Value::make_number(123)};
    EXPECT_THROW(len_string_builtin(non_string_args), std::runtime_error);
}

// Test for empty function
TEST(BuiltinStringTest, Empty) {
    // Test with empty string
    std::vector<Value> args = {Value::string("")};
    Value result = empty_builtin(args);
    EXPECT_TRUE(result.is_boolean());
    EXPECT_TRUE(result.as_boolean());
    
    // Test with non-empty string
    args[0] = Value::string("hello");
    result = empty_builtin(args);
    EXPECT_TRUE(result.is_boolean());
    EXPECT_FALSE(result.as_boolean());
    
    // Test with wrong number of arguments
    std::vector<Value> empty_args;
    EXPECT_THROW(empty_builtin(empty_args), std::runtime_error);
    
    std::vector<Value> too_many_args = {Value::string("test"), Value::string("test2")};
    EXPECT_THROW(empty_builtin(too_many_args), std::runtime_error);
    
    // Test with non-string argument
    std::vector<Value> non_string_args = {Value::make_number(123)};
    EXPECT_THROW(empty_builtin(non_string_args), std::runtime_error);
}

// Test for stridx function
TEST(BuiltinStringTest, Stridx) {
    // Test basic functionality
    std::vector<Value> args = {Value::string("hello world"), Value::string("world")};
    Value result = stridx_builtin(args);
    EXPECT_TRUE(result.is_number());
    EXPECT_EQ(result.as_number(), 7); // VimL index starts from 1
    
    // Test not found
    args[1] = Value::string("xyz");
    result = stridx_builtin(args);
    EXPECT_TRUE(result.is_number());
    EXPECT_EQ(result.as_number(), -1);
    
    // Test with start position
    args = {Value::string("hello hello"), Value::string("hello"), Value::make_number(2)};
    result = stridx_builtin(args);
    EXPECT_TRUE(result.is_number());
    EXPECT_EQ(result.as_number(), 7); // Second occurrence
    
    // Test with wrong number of arguments
    std::vector<Value> too_few_args = {Value::string("test")};
    EXPECT_THROW(stridx_builtin(too_few_args), std::runtime_error);
    
    std::vector<Value> too_many_args = {Value::string("test"), Value::string("test"), Value::make_number(1), Value::string("extra")};
    EXPECT_THROW(stridx_builtin(too_many_args), std::runtime_error);
    
    // Test with non-string arguments
    std::vector<Value> non_string_args = {Value::make_number(123), Value::string("test")};
    EXPECT_THROW(stridx_builtin(non_string_args), std::runtime_error);
}

// Test for strridx function
TEST(BuiltinStringTest, Strridx) {
    // Test basic functionality
    std::vector<Value> args = {Value::string("hello world hello"), Value::string("hello")};
    Value result = strridx_builtin(args);
    EXPECT_TRUE(result.is_number());
    EXPECT_EQ(result.as_number(), 13); // Last occurrence, VimL index starts from 1
    
    // Test not found
    args[1] = Value::string("xyz");
    result = strridx_builtin(args);
    EXPECT_TRUE(result.is_number());
    EXPECT_EQ(result.as_number(), -1);
    
    // Test with start position limiting the search range
    args = {Value::string("hello hello world"), Value::string("hello"), Value::make_number(10)};
    result = strridx_builtin(args);
    EXPECT_TRUE(result.is_number());
    EXPECT_EQ(result.as_number(), 7); // Second occurrence (limited by start position)
    
    // Test with wrong number of arguments
    std::vector<Value> too_few_args = {Value::string("test")};
    EXPECT_THROW(strridx_builtin(too_few_args), std::runtime_error);
    
    std::vector<Value> too_many_args = {Value::string("test"), Value::string("test"), Value::make_number(1), Value::string("extra")};
    EXPECT_THROW(strridx_builtin(too_many_args), std::runtime_error);
    
    // Test with non-string arguments
    std::vector<Value> non_string_args = {Value::make_number(123), Value::string("test")};
    EXPECT_THROW(strridx_builtin(non_string_args), std::runtime_error);
}

// Test for strpart function
TEST(BuiltinStringTest, Strpart) {
    // Test basic functionality
    std::vector<Value> args = {Value::string("hello world"), Value::make_number(7)};
    Value result = strpart_builtin(args);
    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(result.as_string(), "world");
    
    // Test with length parameter
    args = {Value::string("hello world"), Value::make_number(1), Value::make_number(5)};
    result = strpart_builtin(args);
    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(result.as_string(), "hello");
    
    // Test with negative start
    args = {Value::string("hello world"), Value::make_number(-5)};
    result = strpart_builtin(args);
    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(result.as_string(), "world");
    
    // Test with wrong number of arguments
    std::vector<Value> too_few_args = {Value::string("test")};
    EXPECT_THROW(strpart_builtin(too_few_args), std::runtime_error);
    
    std::vector<Value> too_many_args = {Value::string("test"), Value::make_number(1), Value::make_number(2), Value::make_number(3), Value::string("extra")};
    EXPECT_THROW(strpart_builtin(too_many_args), std::runtime_error);
    
    // Test with non-string argument
    std::vector<Value> non_string_args = {Value::make_number(123), Value::make_number(1)};
    EXPECT_THROW(strpart_builtin(non_string_args), std::runtime_error);
}

// Test for tolower function
TEST(BuiltinStringTest, Tolower) {
    // Test basic functionality
    std::vector<Value> args = {Value::string("HELLO WORLD")};
    Value result = tolower_builtin(args);
    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(result.as_string(), "hello world");
    
    // Test with mixed case
    args[0] = Value::string("HeLLo WoRLd");
    result = tolower_builtin(args);
    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(result.as_string(), "hello world");
    
    // Test with non-ASCII characters
    args[0] = Value::string("HELLO 世界");
    result = tolower_builtin(args);
    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(result.as_string(), "hello 世界");
    
    // Test with wrong number of arguments
    std::vector<Value> empty_args;
    EXPECT_THROW(tolower_builtin(empty_args), std::runtime_error);
    
    std::vector<Value> too_many_args = {Value::string("test"), Value::string("test2")};
    EXPECT_THROW(tolower_builtin(too_many_args), std::runtime_error);
    
    // Test with non-string argument
    std::vector<Value> non_string_args = {Value::make_number(123)};
    EXPECT_THROW(tolower_builtin(non_string_args), std::runtime_error);
}

// Test for toupper function
TEST(BuiltinStringTest, Toupper) {
    // Test basic functionality
    std::vector<Value> args = {Value::string("hello world")};
    Value result = toupper_builtin(args);
    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(result.as_string(), "HELLO WORLD");
    
    // Test with mixed case
    args[0] = Value::string("HeLLo WoRLd");
    result = toupper_builtin(args);
    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(result.as_string(), "HELLO WORLD");
    
    // Test with non-ASCII characters
    args[0] = Value::string("hello 世界");
    result = toupper_builtin(args);
    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(result.as_string(), "HELLO 世界");
    
    // Test with wrong number of arguments
    std::vector<Value> empty_args;
    EXPECT_THROW(toupper_builtin(empty_args), std::runtime_error);
    
    std::vector<Value> too_many_args = {Value::string("test"), Value::string("test2")};
    EXPECT_THROW(toupper_builtin(too_many_args), std::runtime_error);
    
    // Test with non-string argument
    std::vector<Value> non_string_args = {Value::make_number(123)};
    EXPECT_THROW(toupper_builtin(non_string_args), std::runtime_error);
}

// Test for substitute function
TEST(BuiltinStringTest, Substitute) {
    // Test basic functionality
    std::vector<Value> args = {Value::string("hello world"), Value::string("world"), Value::string("VimL")};
    Value result = substitute_builtin(args);
    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(result.as_string(), "hello VimL");
    
    // Test with global flag
    args = {Value::string("hello world hello"), Value::string("hello"), Value::string("hi"), Value::string("g")};
    result = substitute_builtin(args);
    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(result.as_string(), "hi world hi");
    
    // Test with case insensitive flag
    args = {Value::string("Hello World"), Value::string("hello"), Value::string("hi"), Value::string("i")};
    result = substitute_builtin(args);
    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(result.as_string(), "hi World");
    
    // Test with both flags
    args = {Value::string("Hello hello"), Value::string("hello"), Value::string("hi"), Value::string("gi")};
    result = substitute_builtin(args);
    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(result.as_string(), "hi hi");
    
    // Test with wrong number of arguments
    std::vector<Value> too_few_args = {Value::string("test"), Value::string("test")};
    EXPECT_THROW(substitute_builtin(too_few_args), std::runtime_error);
    
    std::vector<Value> too_many_args = {Value::string("test"), Value::string("test"), Value::string("test"), Value::string("g"), Value::string("extra")};
    EXPECT_THROW(substitute_builtin(too_many_args), std::runtime_error);
    
    // Test with non-string arguments
    std::vector<Value> non_string_args = {Value::make_number(123), Value::string("test"), Value::string("test")};
    EXPECT_THROW(substitute_builtin(non_string_args), std::runtime_error);
}

// Test for split function
TEST(BuiltinStringTest, Split) {
    // Test basic functionality
    std::vector<Value> args = {Value::string("hello world test")};
    Value result = split_builtin(args);
    EXPECT_TRUE(result.is_list());
    auto& list = result.as_list();
    EXPECT_EQ(list.size(), 3);
    EXPECT_EQ(list[0].as_string(), "hello");
    EXPECT_EQ(list[1].as_string(), "world");
    EXPECT_EQ(list[2].as_string(), "test");
    
    // Test with custom separator
    args = {Value::string("hello,world,test"), Value::string(",")};
    result = split_builtin(args);
    EXPECT_TRUE(result.is_list());
    list = result.as_list();
    EXPECT_EQ(list.size(), 3);
    EXPECT_EQ(list[0].as_string(), "hello");
    EXPECT_EQ(list[1].as_string(), "world");
    EXPECT_EQ(list[2].as_string(), "test");
    
    // Test with keepempty flag
    args = {Value::string("hello,,world"), Value::string(","), Value::boolean(true)};
    result = split_builtin(args);
    EXPECT_TRUE(result.is_list());
    list = result.as_list();
    EXPECT_EQ(list.size(), 3);
    EXPECT_EQ(list[0].as_string(), "hello");
    EXPECT_EQ(list[1].as_string(), "");
    EXPECT_EQ(list[2].as_string(), "world");
    
    // Test with wrong number of arguments
    std::vector<Value> too_many_args = {Value::string("test"), Value::string(","), Value::boolean(true), Value::string("extra")};
    EXPECT_THROW(split_builtin(too_many_args), std::runtime_error);
    
    // Test with non-string first argument
    std::vector<Value> non_string_args = {Value::make_number(123)};
    EXPECT_THROW(split_builtin(non_string_args), std::runtime_error);
}

// Test for join function
TEST(BuiltinStringTest, Join) {
    // Create a list
    Value list_val = Value::list();
    auto& list = list_val.as_list();
    list.push_back(Value::string("hello"));
    list.push_back(Value::make_number(123));
    list.push_back(Value::boolean(true));
    
    // Test basic functionality
    std::vector<Value> args = {list_val};
    Value result = join_builtin(args);
    EXPECT_TRUE(result.is_string());
    // Boolean true should be converted to "1" by Value::to_string()
    EXPECT_EQ(result.as_string(), "hello 123 1");
    
    // Test with custom separator
    args = {list_val, Value::string(",")};
    result = join_builtin(args);
    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(result.as_string(), "hello,123,1");
    
    // Test with different value types in list
    list.clear();
    list.push_back(Value::string("hello"));
    list.push_back(Value::make_number(123));
    list.push_back(Value::boolean(false));
    
    args = {list_val, Value::string("|")};
    result = join_builtin(args);
    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(result.as_string(), "hello|123|0");
    
    // Test with wrong number of arguments
    std::vector<Value> too_few_args;
    EXPECT_THROW(join_builtin(too_few_args), std::runtime_error);
    
    std::vector<Value> too_many_args = {list_val, Value::string(","), Value::string("extra")};
    EXPECT_THROW(join_builtin(too_many_args), std::runtime_error);
    
    // Test with non-list first argument
    std::vector<Value> non_list_args = {Value::make_number(123)};
    EXPECT_THROW(join_builtin(non_list_args), std::runtime_error);
}

// Test for string function
TEST(BuiltinStringTest, String) {
    // Test with string
    std::vector<Value> args = {Value::string("hello")};
    Value result = string_builtin(args);
    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(result.as_string(), "hello");
    
    // Test with number
    args[0] = Value::make_number(123);
    result = string_builtin(args);
    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(result.as_string(), "123");
    
    // Test with boolean true
    args[0] = Value::boolean(true);
    result = string_builtin(args);
    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(result.as_string(), "1");
    
    // Test with boolean false
    args[0] = Value::boolean(false);
    result = string_builtin(args);
    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(result.as_string(), "0");
    
    // Test with list
    Value list_val = Value::list();
    auto& list = list_val.as_list();
    list.push_back(Value::string("hello"));
    list.push_back(Value::make_number(123));
    args[0] = list_val;
    result = string_builtin(args);
    EXPECT_TRUE(result.is_string());
    // The exact format may vary, but it should contain the list elements
    EXPECT_FALSE(result.as_string().empty());
    
    // Test with wrong number of arguments
    std::vector<Value> empty_args;
    EXPECT_THROW(string_builtin(empty_args), std::runtime_error);
    
    std::vector<Value> too_many_args = {Value::string("test"), Value::string("test2")};
    EXPECT_THROW(string_builtin(too_many_args), std::runtime_error);
}

} // namespace viml